﻿using System;
using System.Linq;

using Buddy.BehaviorTree;
using Buddy.CommonBot;
using Buddy.Swtor;
using Buddy.Swtor.Objects;

using WingIt;
using WingIt.Dynamics;
using WingIt.Routines.Mirrors;

using Action = Buddy.BehaviorTree.Action;

namespace WingIt.Routines
{
    [RevisionControlId("$Id: Medic_Sawbones.cs 696 2012-08-13 17:20:41Z Chinajade $")]
    public class OperativeMedic_ScoundrelSawbones : MirrorRoutine
    {
        // MAINTAINERS NOTES:
        // * You will notice that lambda expression argument names are unique (e.g., "darkWardOn", "darkWardWhen", "joltWhen"),
        //   instead of generic (e.g., "ctx", "ret", "on", "doWhen", "castWhen").  PLEASE honor this convention!
        //   This allows us to quickly localize problems when looking at user's logs.  When the BehaviorTree encounters an exception
        //   (e.g., "object not found"), the location where an exception is thrown is identifiable only through the name
        //   of the lambda expression argument.  Such is the nature of BehaviorTrees.

        #region Convenience Methods
        private static bool MeHasTacticalAdvantage { get { return Me.HasMirrorBuff("Tactical Advantage"); } }
        private static bool MeHasTacticalAdvantageStacks { get { return Me.HasMirrorBuffStacks("Tactical Advantage", 2); } }
        #endregion

        [Behavior(BehaviorType.Pull)]
        [Class(CharacterClass.Agent, AdvancedClass.Operative, SkillTreeId.OperativeMedic)]
        [Class(CharacterClass.Smuggler, AdvancedClass.Scoundrel, SkillTreeId.ScoundrelSawbones)]
        public static Composite OperativeMedic_ScoundrelSawbones_Pull()
        {
            return new PrioritySelector(
                LazyRaider.CreateBTPS_NotifyAndShortCircuitWhenCombatDisabled(),
                CreateBTPS.BWcoreFixup_WaitForHarvestComplete(),
                TargetSelect.CreateBTPS_UpdateForBattlefieldConditions(),

                new Decorator(whenCrowdControlNeeded => CCTarget != null,
                    Cast("Slice Droid", sliceDroidOn => CCTarget, sliceDroidWhen => TargetSelect.ViableTargetList().FirstOrDefault(t => t.HasMirrorDebuffStacks("Slice Droid")) == null)),
                new Decorator(whenInParty => Me.IsInParty(), new Action(alwaysSucceed => RunStatus.Success)),
                new Decorator(whenNotInParty => !Me.IsInParty(), OperativeMedic_ScoundrelSawbones_Combat())
            );
        }

        [Behavior(BehaviorType.Combat)]
        [Class(CharacterClass.Agent, AdvancedClass.Operative, SkillTreeId.OperativeMedic)]
        [Class(CharacterClass.Smuggler, AdvancedClass.Scoundrel, SkillTreeId.ScoundrelSawbones)]
        public static Composite OperativeMedic_ScoundrelSawbones_Combat()
        {
            return new PrioritySelector(
                LazyRaider.CreateBTPS_NotifyAndShortCircuitWhenCombatDisabled(),
                TargetSelect.CreateBTPS_UpdateForBattlefieldConditions(),

                // If in group, stay in range of HealTarget; otherwise, stay in range of MyTarget...
                CreateBTPS.StopWithinRangeAndLoS(target => ((Me.IsInParty() && (HealTarget != null)) ? HealTarget : MyTarget), Global.Distance.Ranged),

                // General
                Cast("Escape", escapeWhen => Global.CCBreak), // Escape
                Cast("Evasion", evasionWhen => Me.HealthPercent <= Global.Health.OhShit), // Dodge
                Cast("Cloaking Screen", cloakingScreenWhen => Me.HealthPercent <= Global.Health.OhShit && Me.CanMirrorCast("Evasion", Me)), // Disappearing Act
                Cast("Stim Boost", stimBoostWhen => MeHasTacticalAdvantage), // Pugnacity
                Cast("Adrenaline Probe", adrenalineProbeWhen => Resource <= 50), // Cool Head

                // CC
                new Decorator(whenCrowdControlNeeded => CCTarget != null,
                    Cast("Slice Droid", sliceDroidOn => CCTarget, sliceDroidWhen => TargetSelect.ViableTargetList().FirstOrDefault(t => t.HasMirrorDebuffStacks("Slice Droid")) == null)),
                Cast("Shield Probe", shieldProbeWhen => Me.HealthPercent <= Global.Health.High), // Defense Screen
                Cast("Flash Bang", flashBangWhen => MyTarget.MobCountAround(Global.Distance.MeleeAoE) >= 4),
                Cast("Debilitate", debilitateWhen => Global.RequiresCrowdControl), // Dirty Kick

                // Healing Rotation
                new Decorator(whenHealNeeded => HealTarget != null,
                    new PrioritySelector(
                        //CreateBTPS.CrouchForTarget(myTarget => HealTarget),
                        Cast("Surgical Probe", surgicalProbeOn => HealTarget, surgicalProbeWhen => MeHasTacticalAdvantage && HealTarget.HealthPercent <= Global.Health.Low),
                        //CastOnGround("Recuperative Nanotech", recuperativeNanotechOn => Helpers.PlayersInGroup().FirstOrDefault(p => p.HealthPercent <= Global.MidHealth).Position, recuperativeNanotechWhen => Helpers.PlayersInGroup(p => p.HealthPercent <= Global.MidHealth && p.IsInRange(1f)).Count() >= 3),//AoE Heal
                        Cast("Diagnostic Scan", diagnosticScanOn => HealTarget, diagnosticScanWhen => Resource < 60 || (Me.IsInParty() && HealTarget.HealthPercent > Global.Health.High && HealTarget.HealthPercent < Global.Health.Full)),
                        Cast("Kolto Probe", koltoProbeOn => HealTarget, koltoProbeWhen => !HealTarget.HasMirrorBuffStacks("Kolto Probe", 2, b => b.TimeLeft >= TimeSpan.FromSeconds(3))),
                        Cast("Kolto Infusion", koltoInfusionOn => HealTarget, koltoInfusionWhen => MeHasTacticalAdvantage && HealTarget.HealthPercent <= Global.Health.Mid && Resource >= 67),
                        Cast("Kolto Injection", koltoInjectionOn => HealTarget, koltoInjectionWhen => HealTarget.HealthPercent <= Global.Health.High && Resource >= 67)
                        )),

                // Damage Rotation
                new Decorator(whenSoloing => !Me.IsInParty(),
                    new PrioritySelector
                    (
                        // TODO: Please explain why this is here--its not immediately obvious.
                        new Decorator(whenLowHealth => HealTarget.HealthPercent <= Global.Health.High,
                            new Action(delegate { return RunStatus.Failure; })),
                        Cast("Fragmentation Grenade", fragmentationGrenadeOn => MyTarget, fragmentationGrenadeWhen => MyTarget.MobCountAround(Global.Distance.MeleeAoE) >= 3),
                        CreateBTPS.MoveAndCoverForTarget(myTarget => MyTarget),
                        Cast("Carbine Burst", carbineBurstOn => MyTarget, carbineBurstWhen => MyTarget.MobCountAround(Global.Distance.MeleeAoE) >= 3 && Resource >= 80),
                        Cast("Overload Shot", overloadShotOn => MyTarget, overloadShotWhen => !Me.IsInCover() && Resource >= 80),
                        Cast("Explosive Probe", explsiveProbeOn => MyTarget, explosiveProbeWhen => Resource >= 80),
                        Cast("Snipe", snipeWhen => Me.IsInCover() && Resource >= 80),
                        Cast("Shiv", shivWhen => MyTarget.IsInRange(Global.Distance.Melee)),
                        Cast("Corrosive Dart", corrosiveDartWhen => !MyTarget.HasMirrorDebuff("Poisoned (Tech)") && Global.RequiresCrowdControl),
                        Cast("Rifle Shot")
                    )),

                // Movement
                CreateBTPS.MoveWithinRangeAndLoS(target => ((Me.IsInParty() && (HealTarget != null)) ? HealTarget : MyTarget), Global.Distance.Ranged)
                );
        }

        [Behavior(BehaviorType.OutOfCombat)]
        [Class(CharacterClass.Agent, AdvancedClass.Operative, SkillTreeId.OperativeMedic)]
        [Class(CharacterClass.Smuggler, AdvancedClass.Scoundrel, SkillTreeId.ScoundrelSawbones)]
        public static Composite OperativeMedic_ScoundrelSawbones_OutOfCombat()
        {
            // NB: Apparently, BWcore will call this routine when in combat on occasion.  It is also called while we're pulling.
            return (new PrioritySelector(
                TargetSelect.CreateBTPS_UpdateForBattlefieldConditions(),
                new Decorator(oocBuffWhen => !Me.IsMounted,
                    new PrioritySelector(
                        // Only break cover when we're not in pull...
                        new Decorator(breakCoverWhen => !Me.InCombat && !TargetSelect.IsAtThePull(),
                            CreateBTPS.BreakCrouchOrCover()),     // Prevents the 'stuck handler dance' if combat ends in crouch/cover
                        // Heal slacking party members, after battle...
                        // NB: We only need to do this when in a party, because our Rejuvenation will take care of the needs
                        // of self and companion.
                        new Decorator(whenPartyNeedsHeal => Me.IsInParty() && (HealTarget != null),
                            OperativeMedic_ScoundrelSawbones_Combat())
                        ))
                ));
        }
    }
}